package okw.manager;

import java.net.BindException;
import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import okw.communication.CommunicationService;
import okw.database.DatabaseService;
import okw.database.model.Node;
import okw.gui.DlgCommunicationState;
import okw.gui.MainWindow;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;

import core.communication.message.Direction;
import core.exceptions.CommunicationInitiationException;
import core.exceptions.PortInUseException;
import core.utils.LocatorUriChecker;

@Controller
public class ManagerControllerImpl implements ManagerController {

	private static final Logger L = LoggerFactory.getLogger(ManagerControllerImpl.class);

	private MainWindow mainWindow;
	private DatabaseService databaseService;
	private CommunicationService communicationService;

	@Inject
	public ManagerControllerImpl(MainWindow mainWindow, DatabaseService databaseService, CommunicationService communicationService) {
		L.debug("Tworzę ManagerController");

		Assert.notNull(mainWindow);
		Assert.notNull(databaseService);
		Assert.notNull(communicationService);

		this.mainWindow = mainWindow;
		this.databaseService = databaseService;
		this.communicationService = communicationService;

		L.debug("Utworzono ManagerController");
	}

	@Override
	public void setUp() throws CommunicationInitiationException, PortInUseException {
		L.debug("setUp");

		Node node = databaseService.getMainNode();

		if (node == null) {
			node = mainWindow.askForMainNodeData();
			if (!LocatorUriChecker.checkIfLocatorUriIsOpen(node.getIp(), node.getPort())) {
				L.error("Wrong server locatorURI.");
				throw new CommunicationInitiationException("Wrong server locatorURI.");
			}
			node.setLevel(0);
			databaseService.storeNode(node);
		}
		try {
			communicationService.setUp();
		} catch (BindException e) {
			if (!LocatorUriChecker.checkIfLocatorUriIsOpen(node.getIp(), node.getPort())) {
				databaseService.deleteNode(node);
				L.error("Wrong server locatorURI.");
				throw new CommunicationInitiationException("Wrong server locatorURI.");
			} else {
				L.error("The port is already in use.");
				throw new PortInUseException("The port is already in use.");
			}
		}
	}

	@Override
	public void shutDown() {
		L.debug("shutDown");

		communicationService.shutDown();
	}

	@Override
	public Boolean isNodeLocatorUriTaken(String ip, String port) {
		return databaseService.isNodeLocatorUriTaken(ip, port);
	}

	@Override
	public List<Node> getNeighborNodes() {
		List<Node> returnList = new ArrayList<Node>();
		returnList.addAll(databaseService.getNodesForDirection(Direction.UP));
		returnList.addAll(databaseService.getNodesForDirection(Direction.DOWN));
		return returnList;
	}

	@Override
	public void checkCommunication(DlgCommunicationState dlgCommunicationState) {
		communicationService.checkCommunication(dlgCommunicationState);
	}

	@Override
	public Node getMainNode() {
		return databaseService.getMainNode();
	}
}
